/*
 * Engine object contains many functions, but the basic
 * principle is simple; engine object takes care of UI functionality:
 * It shows and hides the views when needed,
 * and stores the view history for back button
 * 
 * Engine object is created in App.js (qype.engine)
 * 
 */
var Engine = Class.create({
	// Init function, nothing specific in here
    initialize: function() {
		// Views are defined through registerViews function
		// in qype.init function in App.js 
        this.views = {}; 
        this.activeViewId = undefined; // Used to store the current view
		this.activeView = undefined; // Same as previous, but the name instead of id
        this.location = {}; // Stores current location
        
        this.navigation = []; // Navigation stack array for back button history

        this.startViewId = "placesView"; // Sets the initial view
        this.startViewParams = undefined; // No parameters this time

        this.mainButtons = {
            left: util.el("mainButtonLeft"), // Gets the DOM element for the left header button
            right: util.el("mainButtonRight") // Gets the DOM element for the right header button
        }

        this.contentLang = "en"; // Defines content language

		/*
		 * Okay, lot's of basic stuff, but this one's interesting:
		 * We are using our own event handler for buttons also.
		 * Here we subscribe to listen the events from the main buttons
		 * and associate a callback function for that
		 */
        EventManager.subscribe("mainButtonClick", this.onMainButtonClick, this);
    },

	/* This is called when we have received a successful location
	 * either from GPS or manually in app.js init
	 */ 
    startup: function(position) {
		// Stores the received location to engine's variable
        this.setCurrentLocation(position);

		// Clean up view history
		this.navigation = [];

		// Sets the start view (PlacesView, defined in engine init)
		// to become visible
        this.activateView(this.startViewId, this.startViewParams);
    },

	// Return content language
    getContentLang: function() {
        return this.contentLang;
    },
 
    /**
     * registerViews is called from init function in App.js
     * It adds all the specified views to engine
     * 
     * If the needed view is completely static and thus there's no need to create
     * a new View child class but still the view should be handled as a View, one can
     * register it with value undefined (or give the classname View).
     *
     * @param {Object} views Hash containing domId -> class name (*View) mappings. Based
     *                       on the hash, new views are created, inited and registered.
     */
    registerViews: function(views) {
        views = views || {};
        var view = undefined;
		
		// Loop through all the given views
        for(var id in views) {
            if(views[id]) {
                view = new views[id](id);
            }
            else { // fallback to the plain View
                view = new View(id);
            }

			// In view js-files, engine can be referenced as this.engine
            view.engine = this;
            this.views[id] = view;

			// When view is added to engine, it is set to default
			// state (hidden)
            this.hideView(id);
        }
    },

	// 'Mother' function for hideView; calls to hide the view,
	// but also cleans up the data of current active view 
    deactivateView: function(viewId) {
        util.log("{Engine.prototype.deactivateView} start");
        if(this.isViewActive(viewId)) {
            this.hideView(viewId);

            this.views[viewId].deactivate();
			this.activeView = undefined;
        	this.activeViewId = undefined;
        }
    },

    /**
     * Unlike deactivateView(), activateView function contains
     * more features; instead of just showing the give view,
     * activateView needs to take care of adding the view to
     * history stack for back button
     * 
     * If activating an already active view, it is done normally -> allows view
     * refreshing.
     */
    activateView: function(viewId, params) {
        util.log("{Engine.prototype.activateView} start");
		
		// Store existing view to be added to history stack
        var previousViewId = this.activeViewId;
        var previousViewParams = this.activeView ? this.activeView.activationParams : undefined;

		// Call to deactivate the previously active view
        if(this.activeViewId !== undefined) {
            this.deactivateView(this.activeViewId);
        }

		// Call for view-specific activate function
        this.views[viewId].activate(params);

        if(!params || !params.excludeFromNavigation) {
            // If sequential activations of the same view with same parameters,
            // pop the previous out of the stack.
            if(viewId === previousViewId && params === previousViewParams) {
                this.navigation.pop();
            }
            
			// Add new view to navigation stack
            this.navigation.push({viewId: viewId, params: params});

			// Set back button according the lenght of the navigation stack;
			// If there's no history data, change button to 'Exit'. Otherwise,
			// show 'Back'
            (this.navigation.length > 1) ?
                this.mainButtons.right.addClassName("back").removeClassName("exit") :
                this.mainButtons.right.addClassName("exit").removeClassName("back");
        }
		
		// Set new current active view
		this.activeView = this.views[viewId];
        this.activeViewId = viewId;

		// Finally, call for showView to actually display the view
        this.showView(viewId);
    },

	// onMainButtonClick receives button events (as registered in
	// engine's initialize function
    onMainButtonClick: function(event) {
        if(event) {
			
			// Handler for back button
            if(event.type == 'right') {
				// Special case: Exit button is displayed when
				// no history data exists, so the functionality
				// is executed accordingly
                if(this.navigation.length <= 1) {
                    this.exit();
                }
                else {
					// Normally, step back in navigation stack
                    this.back();
                }
            }
            else {
                // Left button takes to Home and clear also the navigation stack
                // in order to show the exit button
                this.navigation = [];
                this.activateView(this.startViewId, this.startViewParams);
            }
        }
    },

	// Quit the app
    exit: function() {
        util.log("{Engine.exit} exiting");
        window.close();
    },

	// Step back in navigation (history) stack
    back: function() {
        if(this.navigation.length > 1) {
            // The current view is on the top, so ditch it
            this.navigation.pop();

			// Set the next view in the stack as active
            var item = this.navigation.pop();
            this.activateView(item.viewId, item.params);
        }
    },

	// Used for checking if the given view is currently active
    isViewActive: function(viewId) {
        util.log("{Engine.prototype.isViewActive} start");
        return (this.activeViewId !== undefined && this.activeViewId === viewId);
    },

	// Hides the view
    hideView: function(viewId) {
        this.toggleView(viewId, false);
    },

	// Shows the view
    showView: function(viewId) {
        this.toggleView(viewId, true);
    },

    /**
     * toggleView takes care of the actual work of hiding/showing
     * view divs defined in index.html
     * 
     * @param {string} viewId Name of the view to process
     * @param {bool} visibility Force showing/hiding the view. If not given, the visibility
     *                          is toggled between 'block'/'none' (optional)
     */
    toggleView: function(viewId, visibility) {
        var view = util.el(viewId); // Get View DOM element
        if(view && view.style) {
			
			// We're using toggleView for both hiding and showing
			// the view, so that's why there's some additional
			// logics in defining what to do
            var doShow = (visibility === undefined) ?
                    (view.style.display === "none") :
                    visibility;

			// Sets the div's display-value as block or none
            view.style.display = doShow ? "block" : "none";
        }
        else {
			// In case the wrong DOM id is given, output it to log
            util.log("{Engine.prototype.toggleView} element not found ('" + viewId + "')");
        }
    },

	// Stores current location
    setCurrentLocation: function(data) {
        this.location = data;
    },

	// Returns current location
    getCurrentLocation: function() {
        return this.location;
    }
});